package com.ctsi.framework.gateway.filter;

import com.ctsi.commons.util.UtilValidate;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.stream.Stream;

/**
 * @author zhangjw
 * @Date: 2018/9/2 23:47
 * @Description: 拦截header，get post传参，cookies中的Authorization信息（token）
 */
@Component
public class AuthParamsHandlerFilter extends ZuulFilter {

    private static final Logger logger = LoggerFactory.getLogger(AuthParamsHandlerFilter.class);

    private static final String BEARER_TOKEN_TYPE = "bearer ";
    private static final String BASIC_TOKEN_TYPE = "Basic ";
    private static final String PARAM_TOKEN_NAME = "x-access-token";
    /**
     * to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
     * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
     * We also support a "static" type for static responses see  StaticResponseFilter.
     * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
     *
     * @return A String representing that type
     */
    @Override
    public String filterType() {
         return "pre";
    }

    /**
     * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
     * important for a filter. filterOrders do not need to be sequential.
     *
     * @return the int order of a filter
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * a "true" return from this method means that the run() method should be invoked
     *
     * @return true if the run() method should be invoked. false will not invoke the run() method
     */
    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
        if(StringUtils.isNotEmpty(authHeader)){
            logger.info("auth filter will be not running!");
            return false;
        }
        logger.info("auth filter will be running!");
        return true;
    }

    /**
     * if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
     *
     * @return Some arbitrary artifact may be returned. Current implementation ignores it.
     * @throws ZuulException if an error occurs during execution.
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        Enumeration<String> names = request.getParameterNames();
        while (names.hasMoreElements()){
            String name = names.nextElement();
            if(name.equals(PARAM_TOKEN_NAME)){
                String token = request.getParameter(PARAM_TOKEN_NAME);

                ctx.addZuulRequestHeader(HttpHeaders.AUTHORIZATION, BEARER_TOKEN_TYPE.concat(token));
                return null;
            }
        }

        Cookie[] cks = request.getCookies();
        if (UtilValidate.isNotEmpty(cks)) {
            Stream.of(cks).filter(ck -> ck.getName().equals(PARAM_TOKEN_NAME)).findAny().ifPresent(ck -> {
                String token = ck.getValue();
                ctx.addZuulRequestHeader(HttpHeaders.AUTHORIZATION, BEARER_TOKEN_TYPE.concat(token));
            });
        }
        return null;
    }

}
